//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Eas.WebCrawler.Interfaces;
using Eas.WebCrawler.Model.Core;
namespace Eas.WebCrawler.Test.Model {
    /// <summary>
    /// Summary description for EventReporterTest
    /// </summary>
    [TestClass]
    public class EventReporterTest {
        public EventReporterTest() {
            //
            // TODO: Add constructor logic here
            //
        }

        EventReporterTestHandler _TestHandler;

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        [TestInitialize()]
        public void MyTestInitialize() {
            _TestHandler = new EventReporterTestHandler();
        }

        //
        // Use TestCleanup to run code after each test has run
        [TestCleanup()]
        public void MyTestCleanup() {
            _TestHandler.Check();
            _TestHandler = null;
            GC.Collect();
            GC.Collect();
        }

        //
        #endregion

        [TestMethod]
        /// <summary>Executes the TestBeforeStartEvent test.</summary>
        public void TestBeforeStartEvent() {
            Fire_BeforeStart(false);
            Fire_BeforeStart(true);
        }

        [TestMethod]
        /// <summary>Executes the TestAfterStopEvent test.</summary>
        public void TestAfterStopEvent() {
            Fire_AfterStop(false);
            Fire_AfterStop(true);
        }

        private void Fire_AfterStop(bool fail) {
            if (fail)
                _TestHandler.Events.Stopped += new EventHandler(Event_Throw);
            _TestHandler.Expect(EventTypes.AfterStop);
            _TestHandler.Events.ReportStopped();
        }
        private void Fire_BeforeStart(bool fail) {
            if (fail)
                _TestHandler.Events.Starting += new EventHandler(Event_Throw);
            _TestHandler.Expect(EventTypes.BeforeStart);
            _TestHandler.Events.ReportStarting();
        }

        void Event_Throw(object sender, EventArgs e) {
            throw new InvalidOperationException();
        }


        [TestMethod]
        /// <summary>Executes the TestBeforeUnPauseEvent test.</summary>
        public void TestBeforeUnPauseEvent() {
            Fire_BeforeUnPause(false);
            Fire_BeforeUnPause(true);
        }

        private void Fire_BeforeUnPause(bool fail) {
            if (fail)
                _TestHandler.Events.Unpausing += new EventHandler(Event_Throw);
            _TestHandler.Expect(EventTypes.BeforeUnPause);
            _TestHandler.Events.ReportUnpausing();
        }

        [TestMethod]
        /// <summary>Executes the TestAfterPause test.</summary>
        public void TestAfterPause() {
            Fire_AfterPause(false);
            Fire_AfterPause(true);
        }

        private void Fire_AfterPause(bool fail) {
            if (fail)
                _TestHandler.Events.Paused += new EventHandler(Event_Throw);
            _TestHandler.Expect(EventTypes.AfterPause);
            _TestHandler.Events.ReportPaused();
        }

        [TestMethod]
        /// <summary>Executes the TestUrlBeganProcessingEvent test.</summary>
        public void TestUrlBeganProcessingEvent() {
            Fire_UrlBeganProcessing(false, 18);
            Fire_UrlBeganProcessing(true, 18);
        }

        private void Fire_UrlBeganProcessing(bool fail, int id) {
            using (BaseProcessingState state = new BaseProcessingState(null, id)) {
                using (Eas.WebCrawler.Model.Core.BaseProcessingState expectedState = new Eas.WebCrawler.Model.Core.BaseProcessingState(null, id)) {
                    if (fail)
                        _TestHandler.Events.UrlBeganProcessing += new EventHandler<ProcessingStateEventArgs>(Throw_ProcessingStateEvent);
                    state.Request = RequestHelper.rYahoo;
                    expectedState.Request = RequestHelper.rYahoo;

                    ProcessingStateEventArgs args = new ProcessingStateEventArgs(state);
                    Assert.AreEqual<int>(id, args.State.Id);
                    Assert.AreEqual<Request>(new Request("http://www.yahoo.com"), args.State.Request);

                    _TestHandler.Expect(EventTypes.UrlBegan);
                    _TestHandler.ExpectedWorkerRequestArgs = new ProcessingStateEventArgs(expectedState);

                    _TestHandler.ExpectedId = id;
                    _TestHandler.Events.ReportWebRequestStarted(state);
                }
            }
        }

        void Throw_ProcessingStateEvent(object sender, ProcessingStateEventArgs e) {
            throw new InvalidOperationException();
        }


        [TestMethod]
        /// <summary>Executes the TestUrlCompletedEvent test.</summary>
        public void TestUrlCompletedEvent() {
            Fire_UrlCompleted(false, 17);
            Fire_UrlCompleted(true, 17);
        }

        private void Fire_UrlCompleted(bool fail, int id) {
            using (NMock2.Mockery mockery = new NMock2.Mockery()) {
                using (BaseProcessingState state = new BaseProcessingState(null, id)) {
                    if (fail)
                        _TestHandler.Events.UrlProcessed += new EventHandler<UrlCompletedEventArgs>(Events_UrlProcessed);
                    Eas.WebCrawler.Interfaces.TemporaryStorage storage = mockery.NewMock<Eas.WebCrawler.Interfaces.TemporaryStorage>();
                    Eas.WebCrawler.Interfaces.WebResponseInterface response = mockery.NewMock<Eas.WebCrawler.Interfaces.WebResponseInterface>();
                    state.IsTextResponse = true;
                    state.Request = RequestHelper.rYahoo;
                    state.WebResponse = response;
                    Eas.WebCrawler.Interfaces.UrlCompletedEventArgs arg = new Eas.WebCrawler.Interfaces.UrlCompletedEventArgs(state, storage);
                    Assert.AreEqual<int>(id, arg.State.Id);
                    Assert.AreEqual<bool>(true, arg.State.IsTextResponse);
                    Assert.AreSame(storage, arg.Storage);
                    Assert.AreSame(response, arg.State.WebResponse);
                    Assert.AreEqual<Eas.WebCrawler.Interfaces.Request>(RequestHelper.rYahoo, arg.State.Request);

                    _TestHandler.ExpectedRWebesponse = response;
                    _TestHandler.Expect(EventTypes.UrlProcessed);
                    _TestHandler.ExpectedUrlArgs = arg;
                    _TestHandler.ExpectedId = id;
                    _TestHandler.Events.ReportRequestComplete(state, storage);

                    mockery.VerifyAllExpectationsHaveBeenMet();
                    storage = null;
                }
            }
        }

        void Events_UrlProcessed(object sender, UrlCompletedEventArgs e) {
            throw new InvalidOperationException();
        }

        [TestMethod]
        /// <summary>Executes the TestReportError1 test.</summary>
        public void TestReportError1() {
            using (BaseProcessingState state = new BaseProcessingState(null, 11)) {
                _TestHandler.Expect(EventTypes.Error);
                _TestHandler.ExpectedString = "none:\terikerik";
                _TestHandler.ExpectedId = 11;
                _TestHandler.Events.ReportError(state, "", new System.Exception("erikerik"));
            }
        }

        [TestMethod]
        /// <summary>Executes the TestReportError2 test.</summary>
        public void TestReportError2() {
            using (BaseProcessingState state = new BaseProcessingState(null, 13)) {
                state.Request = RequestHelper.rGoogle;
                _TestHandler.Expect(EventTypes.Error);
                _TestHandler.ExpectedString = "http://www.google.com/:\terikerik";
                _TestHandler.ExpectedId = 13;
                _TestHandler.Events.ReportError(state, "", new Exception("erikerik"));
            }
        }

        [TestMethod]
        /// <summary>Executes the TestReportError3 test.</summary>
        public void TestReportError3() {
            using (BaseProcessingState state = new BaseProcessingState(null, 13)) {
                state.Request = RequestHelper.rGoogle;
                _TestHandler.Expect(EventTypes.Error);
                _TestHandler.ExpectedString = "http://www.google.com/:\tprefix:\tmessage";
                _TestHandler.ExpectedId = 13;
                _TestHandler.Events.ReportError(state, "prefix", new Exception("message"));
            }
        }

        [TestMethod]
        /// <summary>Executes the TestReportRequestFailure test.</summary>
        public void TestReportRequestFailure() {
            Fire_UrlFailed(false);
            Fire_UrlFailed(true);
        }

        private void Fire_UrlFailed(bool fail) {
            using (BaseProcessingState state = new BaseProcessingState(null, 18)) {
                if (fail)
                    _TestHandler.Events.UrlFailed += new EventHandler<UrlFailedEventArgs>(Events_UrlFailed);
                state.Request = RequestHelper.rGoogle;
                _TestHandler.Expect(EventTypes.UrlFailed);
                _TestHandler.ExpectedId = 18;
                _TestHandler.ExpectedString = "http://www.google.com/:\t404:\terikerik";
                _TestHandler.ExpectedResponseCode = 404;
                _TestHandler.ExpectedRWebesponse = null;
                _TestHandler.ExpectedUrlArgs = new Eas.WebCrawler.Interfaces.UrlCompletedEventArgs(state, null);
                _TestHandler.Events.ReportWebResponseFailure(state, 404, "erikerik");
            }
        }

        void Events_UrlFailed(object sender, UrlFailedEventArgs e) {
            throw new InvalidOperationException();
        }

        [TestMethod]
        /// <summary>Executes the TestTraceEvent1 test.</summary>
        public void TestTraceEvent1() {
            using (BaseProcessingState state = new BaseProcessingState(null, 21)) {
                _TestHandler.Events.TraceLevel = TraceLevel.All;
                Assert.AreEqual<TraceLevel>(TraceLevel.All, _TestHandler.Events.TraceLevel);
                _TestHandler.Expect(EventTypes.Trace);
                _TestHandler.ExpectedString = ":\tnone:\terikerik";
                _TestHandler.ExpectedId = 21;
                _TestHandler.Events.ReportTrace(TraceLevel.Critical, state, "erik{0}", "erik");
            }
        }

        [TestMethod]
        /// <summary>Executes the TestTraceEvent2 test.</summary>
        public void TestTraceEvent2() {
            using (BaseProcessingState state = new BaseProcessingState(null, 22)) {
                state.Request = RequestHelper.rGoogle;
                _TestHandler.Events.TraceLevel = TraceLevel.All;
                _TestHandler.Expect(EventTypes.Trace);
                _TestHandler.ExpectedString = ":\thttp://www.google.com/:\terikerik";
                _TestHandler.ExpectedId = 22;
                _TestHandler.Events.ReportTrace(TraceLevel.Critical, state, "erik{0}", "erik");
            }
        }

        [TestMethod]
        /// <summary>Executes the TestTrace test.</summary>
        public void TestTrace() {
            using (BaseProcessingState state = new BaseProcessingState(null, 23)) {
                _TestHandler.Events.TraceLevel = TraceLevel.All;
                _TestHandler.Expect(EventTypes.Trace);
                _TestHandler.ExpectedString = ":\tnone:\terikerik";
                _TestHandler.ExpectedId = 23;
                _TestHandler.Events.ReportTrace(TraceLevel.Critical, state, "erik{0}", "erik");
            }
        }

        [TestMethod]
        /// <summary>Executes the TestTraceFail test.</summary>
        public void TestTraceFail() {
            _TestHandler.Events.OnTrace += new EventHandler<ProcessingStateMessageEventArgs>(Events_OnTrace);
            using (BaseProcessingState state = new BaseProcessingState(null, 23)) {
                _TestHandler.Events.TraceLevel = TraceLevel.All;
                _TestHandler.Expect(EventTypes.Trace);
                _TestHandler.ExpectedString = ":\tnone:\terikerik";
                _TestHandler.ExpectedId = 23;
                _TestHandler.Events.ReportTrace(TraceLevel.Critical, state, "erik{0}", "erik");
            }
        }

        void Events_OnTrace(object sender, ProcessingStateMessageEventArgs e) {
            throw new InvalidOperationException();
        }


        [TestMethod]
        /// <summary>Executes the TestNoFireTraceBecauseOfLevel test.</summary>
        public void TestNoFireTraceBecauseOfLevel() {
            using (BaseProcessingState state = new BaseProcessingState(null, 23)) {
                _TestHandler.Events.TraceLevel = TraceLevel.HighImpact;
                _TestHandler.Events.ReportTrace(TraceLevel.Detailed, state, "erik{0}", "erik");
            }
        }
    }

    public enum EventTypes { BeforeStart = 0, AfterStop = 1, BeforeUnPause = 2, AfterPause = 3, UrlProcessed = 4, Trace = 5, Error = 6, UrlBegan = 7, UrlFailed = 8 };

    internal class EventReporterTestHandler {
        public Eas.WebCrawler.Model.Events.BaseWebCrawlerEvents Events;
        int[] _EventCounts = new int[(int)EventTypes.UrlFailed + 1];

        public EventReporterTestHandler() {
            Events = new Eas.WebCrawler.Model.Events.BaseWebCrawlerEvents();
            Events.Initialize(null);

            Events.Paused += new EventHandler(_Events_AfterPause);
            Events.Stopped += new EventHandler(_Events_AfterStop);
            Events.Starting += new EventHandler(_Events_BeforeStart);
            Events.Unpausing += new EventHandler(_Events_BeforeUnPause);
            Events.OnError += new EventHandler<ProcessingStateMessageEventArgs>(_Events_OnError);
            Events.OnTrace += new EventHandler<ProcessingStateMessageEventArgs>(_Events_OnTrace);
            Events.UrlProcessed += new EventHandler<Eas.WebCrawler.Interfaces.UrlCompletedEventArgs>(_Events_UrlProcessed);
            Events.UrlFailed += new EventHandler<Eas.WebCrawler.Interfaces.UrlFailedEventArgs>(Events_UrlFailed);
            Events.UrlBeganProcessing += new EventHandler<ProcessingStateEventArgs>(Events_UrlBeganProcessing);
        }

        void Events_UrlBeganProcessing(object sender, ProcessingStateEventArgs e) {
            Assert.AreEqual<int>(e.State.Id, ExpectedId);
            Assert.AreEqual<ProcessingStateEventArgs>(e, ExpectedWorkerRequestArgs);
            Fire(EventTypes.UrlBegan);
        }

        void Events_UrlFailed(object sender, Eas.WebCrawler.Interfaces.UrlFailedEventArgs e) {
            Assert.AreEqual<int>(ExpectedId, e.State.Id);
            Assert.AreEqual<Uri>(ExpectedUrlArgs.State.Request.Uri, e.State.Request.Uri);
            Assert.AreEqual<int>(ExpectedResponseCode, e.ResultCode);
            Assert.AreEqual<string>(ExpectedString, e.ResultDescription);
            Fire(EventTypes.UrlFailed);
        }

        public string ExpectedString;
        public int ExpectedId;
        public Eas.WebCrawler.Interfaces.UrlCompletedEventArgs ExpectedUrlArgs;
        public int ExpectedResponseCode;
        public ProcessingStateEventArgs ExpectedWorkerRequestArgs;
        public WebResponseInterface ExpectedRWebesponse;

        void _Events_UrlProcessed(object sender, Eas.WebCrawler.Interfaces.UrlCompletedEventArgs e) {
            Assert.AreEqual<int>(ExpectedId, e.State.Id);
            Assert.AreEqual<Uri>(ExpectedUrlArgs.State.Request.Uri, e.State.Request.Uri);
            Assert.AreEqual<bool>(ExpectedUrlArgs.State.IsTextResponse, e.State.IsTextResponse);
            Assert.AreSame(ExpectedUrlArgs.Storage, e.Storage);
            Assert.AreSame(ExpectedRWebesponse, e.State.WebResponse);
            Fire(EventTypes.UrlProcessed);
        }

        void _Events_OnTrace(object sender, ProcessingStateMessageEventArgs e) {
            Assert.AreEqual<int>(ExpectedId, e.State.Id);
            Assert.IsTrue(e.Message.StartsWith(ExpectedId.ToString()));
            Assert.IsTrue(e.Message.EndsWith(ExpectedString));
            Fire(EventTypes.Trace);
        }

        void _Events_OnError(object sender, ProcessingStateMessageEventArgs e) {
            Assert.AreEqual<int>(ExpectedId, e.State.Id);
            Assert.AreEqual<string>(ExpectedString, e.Message);
            Fire(EventTypes.Error);
        }

        void _Events_BeforeUnPause(object sender, EventArgs e) {
            Fire(EventTypes.BeforeUnPause);
        }

        void _Events_BeforeStart(object sender, EventArgs e) {
            Fire(EventTypes.BeforeStart);
        }

        void _Events_AfterStop(object sender, EventArgs e) {
            Fire(EventTypes.AfterStop);
        }

        void _Events_AfterPause(object sender, EventArgs e) {
            Fire(EventTypes.AfterPause);
        }

        private void Fire(EventTypes type) {
            _EventCounts[(int)type] = _EventCounts[(int)type] + 1;
        }

        public void Expect(EventTypes type) {
            _EventCounts[(int)type] = _EventCounts[(int)type] - 1;
        }

        public void Check() {
            for (int i = 0; i < _EventCounts.Length; ++i) {
                Assert.AreEqual<int>(0, _EventCounts[i]);
            }
        }
    }

}
